package com.bosssoft.server;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.file.Files;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;

import com.bosssoft.exception.MyException;
import com.bosssoft.observer.ConcreteObserver;
import com.bosssoft.observer.Observer;
import com.bosssoft.observer.Subject;
import com.bosssoft.utils.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

/**
 * @author 戴圣燃
 *
 * @Date  2024/5/26
 * BIO服务端，采用阻塞IO模式进行通信
 */
@Slf4j
public class Server extends ServerSocket{

    private static final int PORT = 8888;
    private static String id;
    private static boolean sendAll = false;
    private static String sendClient = "false";
    private static String command;
    static int countMark = 1;
    private static Random random;
    private static String filePath = "D:\\ruijiwaimai\\bosssoft1\\serverReceivedFiles\\";
    static{
        try{
            random = SecureRandom.getInstanceStrong();
        }catch(NoSuchAlgorithmException e){
            log.info("Exception:{}",e.getMessage());
        }
    }
    public Server() throws IOException {
    }
    private static final ConcurrentHashMap<String, Socket> userList = new ConcurrentHashMap<>();
    /**
     * 主方法，启动BIO服务端
     *
     * @param args 命令行参数
     * @throws IOException IO异常
     */
    public static void main(String[] args) throws IOException {

        // 创建被观察者对象
        Subject subject = new Subject();
        // 创建观察者对象
        Observer observer2 = new ConcreteObserver("Observer 2");
        // 添加观察者
        subject.addObserver(observer2);
        // 被观察者执行操作，触发通知
        subject.start();
        try (ServerSocket server = new ServerSocket(PORT);){
            log.info("===========BIO服务端启动================");
            id = Server.setId(); // 生成并设置客户端ID
            log.info("本服务端id为" + id);
            while (true) {
                Socket socket = server.accept();
                // 创建并启动处理客户端连接的线程
                new BioServerThread(socket, subject).start();
            }
        }


    }
    /**
     * 生成一个6位的随机数字ID。
     *
     * @return 生成的6位数字字符串ID
     */
    private static String setId() {
        StringBuilder sb = new StringBuilder(6);
        for (int i = 0; i < 6; i++) {
            int number = random.nextInt(10); // 生成0-9之间的随机数
            sb.append(number);
        }
        return sb.toString();
    }
    /**
     * BIO Server线程类，用于处理客户端连接
     */
    static class BioServerThread extends Thread {

        private final Socket socket;
        private final Subject subject;
        /**
         * 构造方法，初始化Socket连接对象
         *
         * @param socket  Socket连接对象
         * @param subject 用于通知观察者的主题
         */
        public BioServerThread(Socket socket, Subject subject) {
            this.socket = socket;
            this.subject = subject;
        }

        /**
         * 处理客户端连接的逻辑。
         * 具体包括：接收并解密文件名和内容，写入文件，等待和处理客户端的进一步指令。
         * 该方法不接受参数，也没有返回值。
         */
        @Override
        public void run() {
            try (InputStream inputStream = socket.getInputStream();
                 OutputStream outputStream = socket.getOutputStream();
                 BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
                 PrintStream printStream = new PrintStream(outputStream);){

                UtilsFactory utilsFactory = new UtilsFactory();
                // 解密接收的消息
                String decode = utilsFactory.useUtil("decrypt").useUtil(bufferedReader.readLine());

                // 处理解密后的消息，获取客户端ID和文件信息
                String tmp = StringUtils.substringBefore(decode,"\n");
                String toClient = tmp.substring(0,tmp.length()-6);
                String clientId = tmp.substring(tmp.length()-6,tmp.length());
                if(toClient==null||toClient.length()<3){
                    // 处理普通文件传输逻辑
                    userList.put(clientId,socket);
                    for(Socket user: userList.values()){
                        log.info(String.valueOf(user));
                    }
                    // 解析文件名和内容并写入文件
                    String fileNameTmp = StringUtils.substringBefore(StringUtils.substringAfter(decode,"\n"),"\n");
                    String fileName = filePath + fileNameTmp;
                    String content = StringUtils.substringAfter(StringUtils.substringAfter(decode,"\n"),"\n");
                    log.info("已收到id为" + clientId + "客户端传输文件：" + fileNameTmp);
                    File file = new File(fileName);
                    OutputStream os = Files.newOutputStream(file.toPath());
                    os.write(content.getBytes());

                    // 等待并处理进一步的指令
                    String mark = "error";
                    log.info("请输入指令：");
                    while (!mark.equals("show")) {
                        mark = Subject.scannerInstruct();
                    }
                    boolean sendTo = false;

                    // 等待发送文件的指令
                    while(!sendTo){
                        sleep(100);
                        if(sendAll||sendClient.equals(clientId)){
                            sendTo = true;
                            subject.sendDataToServer(socket, command, id);
                        }
                    }

                }else{
                    // 处理转发文件的逻辑
                    String fileNameTmp = StringUtils.substringBefore(StringUtils.substringAfter(decode,"\n"),"\n");
                    String fileName = filePath + fileNameTmp;
                    String content = StringUtils.substringAfter(StringUtils.substringAfter(decode,"\n"),"\n");
                    log.info("已收到id为" + clientId + "的客户端的转发请求：" + fileNameTmp);
                    File file = new File(fileName);
                    OutputStream os = Files.newOutputStream(file.toPath());
                    os.write(content.getBytes());
                    // 根据接收指令处理文件转发
                    if(toClient.equals("all")){
                        sendAll = true;
                        command = filePath + fileNameTmp;
                    }else if(toClient.length()==6){
                        sendClient = toClient;
                        command = filePath + fileNameTmp;
                    }
                    userList.put(clientId,socket);
                    for(Socket user: userList.values()){
                        log.info(String.valueOf(user));
                    }
                    // 向客户端发送接收完成的消息
                    printStream.println("已发送完成");
                    printStream.flush();
                }
            } catch (IOException | MyException e) {
                e.printStackTrace();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                // 尝试关闭套接字
                countMark+=1;
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }
}
